package cn.edu.cug.cs.gtl.docsrv.txt;

import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.extractor.ShapeExtractor;
import cn.edu.cug.cs.gtl.io.FileDataSplitter;
import cn.edu.cug.cs.gtl.mybatis.BatisWrapper;
import cn.edu.cug.cs.gtl.mybatis.Session;
import cn.edu.cug.cs.gtl.mybatis.mapper.doc.DocAll;
import cn.edu.cug.cs.gtl.mybatis.mapper.txt.*;
import cn.edu.cug.cs.gtl.mybatis.mapper.txt.*;
import cn.edu.cug.cs.gtl.protos.Document;
import cn.edu.cug.cs.gtl.protos.TimeValue;
import cn.edu.cug.cs.gtl.util.ZipUtils;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;

import java.io.File;
import java.math.BigDecimal;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public abstract class TxtManager {
    protected Session session;//开启数据库会话

    /**
     * 构造函数
     * @param session
     */
    public TxtManager(Session session){
        this.session = session;
    }

    /**
     * 设置数据库会话
     * @param s
     */
    public void setSession(Session s){
        this.session=s;
    }

    /**
     * 将一个由txtItem+txtFile组成的图形文档插入数据库
     * @param txtItem 图形元数据信息
     * @param txtFile 图形文件内容
     * @return
     */
    public abstract boolean insert(TxtItem txtItem, TxtFile txtFile);

    /**
     * 将一个由TxtAll组成的图形文档插入数据库
     * @param txtAll 图形文档
     * @return 成功返回true,否则返回false
     */
    public abstract boolean insert(TxtAll txtAll);

    /**
     * 读取本地目录中的所有图形文档，将其插入图形库中
     * @param localDir 本地目录
     * @return 成功返回true,否则返回false
     */
    public abstract boolean insert(String localDir);
//    /**
//     * 读取本地目录中的所有图形文档，将其插入图形库中，同时插入指定的文档库
//     * @param localDir 本地目录
//     * @param docManager 文档数据管理器
//     * @return
//     */
//    @Deprecated //不应该在这个层次进行跨库操作,移植到Managers类中。
//    public abstract boolean insert(String localDir, DocManager docManager);


    /**
     * 根据txtInternalId删除数据库中对应的图形文档记录
     * @param txtItem 必须填充txtInternalId
     * @return
     */
    public abstract boolean delete(TxtItem txtItem);

    /**
     * 根据txtInternalId删除数据库中对应的图形文档记录
     * @param txtAll 必须填充txtInternalId
     * @return
     */
    public abstract boolean delete(TxtAll txtAll);

    /**
     * 根据txtItem+txtFile更新数据库中的图形文档记录
     * @param txtItem 该对象的txtInternalId必须填充
     * @param txtFile 更新文档文件的内容
     * @return 更新成功返回true,否则返回false
     */
    public abstract boolean update(TxtItem txtItem, TxtFile txtFile);

    /**
     * 根据根据txtAll中的信息更新数据库中的记录
     * @param txtAll 该对象的txtInternalId必须填充
     * @return 更新成功返回true,否则返回false
     */
    public abstract boolean update(TxtAll txtAll);

//
//    /**
//     * 从文档库中提取入库时间段[begin,end)之内的图形文档
//     * @param docManager 指定的文档库
//     * @param begin 文档入库时间
//     * @param end  文档入库时间
//     * @return List<TxtAll> txtAll列表
//     */
//    @Deprecated //不应该在这个层次进行跨库操作,移植到Managers类中。
//    public  List<TxtAll> extractTxtDocument(DocManager docManager, TimeValue begin, TimeValue end){
//        List<TxtAll> txtAllList = new ArrayList<>();
//        List<DocAll> docAllList = docManager.queryByAppendDate(begin, end);
//
//        for (DocAll docAll: docAllList){
//            TxtAll txtAll = new TxtAll();
//            TxtUtils.toTxtAll(docAll);
//            txtAllList.add(txtAll);
//        }
//        return txtAllList;
//    }
//
//    /**
//     * 从文档库中提取指定名称和版本的图形文档
//     * @param docManager 指定的文档库
//     * @param title
//     * @param version
//     * @return TxtAll
//     */
//    @Deprecated //不应该在这个层次进行跨库操作,移植到Managers类中。
//    public TxtAll extractTxtDocument(DocManager docManager, String title, String version){
//        List<DocAll> docAllList = docManager.queryByTitle(title);
//        for (DocAll docAll: docAllList){
//            if (docAll.getDocVersion().equals(version)){
//                TxtAll txtAll = new TxtAll();
//                TxtUtils.toTxtAll(docAll);
//                return txtAll;
//            }
//        }
//        return null;
//    }

    /**
     * 根据txtInternalId查询文档
     * @param txtInternalId
     * @return TxtAll
     */
    public abstract TxtAll queryByInternalId(BigDecimal txtInternalId);

    /**
     * 根据文档标题查询文档
     * @param txtEnTitle
     * @return List<txtAll>
     */
    public abstract List<TxtAll> queryByEnTitle(String txtEnTitle);

    /**
     * 根据文档注释查询文档
     * @param txtCnTitle  文档注释
     * @return
     */
    public abstract List<TxtAll> queryByComment(String txtCnTitle);

    /**
     * 根据文档类型查询文档
     * @param txtType
     * @return
     */
    public abstract List<TxtAll> queryByType(String txtType);

    /**
     * 从文档库中提取文档添加时间段为[begin,end)之内的文档
     * @param begin
     * @param end
     * @return
     */
    public abstract List<TxtAll> queryByAppendDate(TimeValue begin, TimeValue end);

    /**
     * 查询得到所有ShapeBlock的边界矩形
     * @return
     */
    public abstract List<Pair<BigDecimal, Envelope>> queryAllEnvelopes();

    /**
     * 根据传入参数在图形库中进行查询
     * {
     *     txtInternalId:0
     *     txtEnTitle:"tomcat"
     *     txtCnTitle:"汤姆猫"
     * }
     * @param parameters
     *     Type(Object)  Name(String)
     *     BigDecimal txtInternalId;
     *
     *     String txtEnTitle;
     *
     *     String txtCnTitle;
     *
     *     String txtUuid;
     *
     *     String txtVersion;
     *
     *     String txtKeywords;
     *
     *     String txtAbstract;
     *
     *     String txtContent;
     *
     *     String txtType;
     *
     *     String txtAuthors;
     *
     *     String txtAffiliation;
     *
     *     String txtTags;
     *
     *     String txtUrl;
     *
     *     Date txtAppendDate;
     *
     *     Date txtUpdateDate;
     *
     *     Date txtCreateDate;
     *
     *     String txtMemo;
     *
     *     BigDecimal txtCatalog;
     *
     *     String txtExtent;
     *
     * @return 返回查询到的文档。
     */
    public  abstract List<TxtAll> query(Map<String,Object> parameters);

    /**
     * 获取数据库内所有的图块的名称
     * @return
     */
    public List<String> getAllBlockNames(){
        if(session.isClosed())
            return null;
        TxtItemMapper txtItemMapper = session.getMapper(TxtItemMapper.class);
        List<TxtItem> ls = txtItemMapper.selectAll();
        if(ls==null || ls.size()==0) return null;
        return ls.stream().map(i->i.getTxtEnTitle()).collect(Collectors.toList());
    }

    public List<TxtCatalog> getCatalogs(){
        if(session.isClosed())
            return null;
        TxtCatalogMapper txtMapper = session.getMapper(TxtCatalogMapper.class);
        List<TxtCatalog> ls = txtMapper.selectAll();
        if(ls==null || ls.size()==0) return null;
        return ls;
    }

    /**
     * 如果catalogName 为 中国/湖北/武汉,则返回的是一个字符串对象的数组，
     * 含有三个元素中国、湖北、武汉，表示三级分类
     * @param catalogInternalId
     * @return
     */
    public String[] getCatalogName(BigDecimal catalogInternalId){
        if(session.isClosed())
            return null;
        TxtCatalogMapper txtMapper = session.getMapper(TxtCatalogMapper.class);
        TxtCatalog ls = txtMapper.selectByPrimaryKey(catalogInternalId);
        String c = ls.getCatalogName();
        if(c==null || c.isEmpty()) return null;
        if(!c.contains("/")){
            String [] ss = new String[1];
            ss[0]=c;
            return ss;
        }
        else{
            return c.split("/");
        }
    }

    /**
     * CatalogOrder用于对分类名称进行排序，以便在前端可以按照这个设置进行显示排序
     * @return
     */
    public String getCatalogOrder(BigDecimal catalogInternalId){
        if(session.isClosed())
            return null;
        TxtCatalogMapper txtMapper = session.getMapper(TxtCatalogMapper.class);
        TxtCatalog ls = txtMapper.selectByPrimaryKey(catalogInternalId);
        return ls.getCatalogOrder();
    }

    /**
     * 根据指定的本地目录，
     * 获取其下的所有子目录，
     * 构建TxtCatalog
     * 例如：传入的参数是/Users/ZhenwenHe/git/data
     * 在该目录下的data目录结构如下：
     *                         spatialdata/wuhan
     *                         spatialdata/beijing
     *                         textdata/wuhan
     *                         textdata/beijing
     * 则会生成7个TxtCatalog,其name分别是：
     *                         data
     *                         data/spatialdata
     *                         data/textdata
     *                         data/spatialdata/wuhan
     *                         data/spatialdata/beijing
     *                         data/textdata/wuhan
     *                         data/textdata/beijing
     * 然后会和SHP_CATALOG中的数据进行比较，
     * 如果存在，则修改其ID和ORDER
     * 如果不存在，则插入SHP_CATALOG中
     * @param localDir
     * @return 返回数据库中，当前目录下所有合格的分类对象
     */
    public List<TxtCatalog> localDirToCatalogs(String localDir ){
        try {
            cn.edu.cug.cs.gtl.io.File f = new cn.edu.cug.cs.gtl.io.File(localDir);
            if(!f.exists()) return null;

            if(session.isClosed())
                session=BatisWrapper.getInstance().getMasterSession();

            TxtCatalogMapper mapper = session.getMapper(TxtCatalogMapper.class);
            if(mapper==null) return null;

            List<TxtCatalog> ls= new ArrayList<>();
            dirToCatalogs(null,localDir, ls);

            //去除数据库中已经存在的分类，并赋值相应的ID
            BigDecimal id = mapper.maxInternalId();
            id = id.add(BigDecimal.ONE);
            List<TxtCatalog> existCatalogs = mapper.selectAll();
            boolean exist = false;
            for(TxtCatalog i: ls){
                for(TxtCatalog j : existCatalogs){
                    exist=false;
                    //如果存在，则直接拷贝已经存在的TxtCatalog
                    if(i.getCatalogName().equals(j.getCatalogName())){
                        i.setCatalogInternalId(j.getCatalogInternalId());
                        if(j.getCatalogOrder()!=null)
                            i.setCatalogOrder(j.getCatalogOrder());
                        exist=true;
                        break;
                    }
                }
                if(exist==false){//如果不存在，插入数据库
                    i.setCatalogInternalId(id);
                    id = id.add(BigDecimal.ONE);
                    mapper.insert(i);
                }
                else{//存在
                    continue;
                }
            }
            session.commit();
            return ls;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 采用递归方法，只供List<TxtCatalog> dirToCatalogs(String localDir )调用
     * @param parentCatalogName
     * @param localDir
     * @param ls
     * @throws Exception
     */
    private   static void dirToCatalogs(
            String parentCatalogName,
            String localDir,
            List<TxtCatalog>ls) throws Exception{

        cn.edu.cug.cs.gtl.io.File f = new cn.edu.cug.cs.gtl.io.File(localDir);
        if(!f.exists()) return;

        if(f.isDirectory()){
            String [] ss = cn.edu.cug.cs.gtl.io.File.split(localDir);
            TxtCatalog txtCatalog = new TxtCatalog();
            //txtCatalog.setCatalogInternalId(txtCatalogMapper.maxInternalId().add(BigDecimal.ONE));
            if(parentCatalogName==null || parentCatalogName.isEmpty()) {
                parentCatalogName = ss[ss.length - 1];
                txtCatalog.setCatalogName(parentCatalogName);
            }
            else {
                parentCatalogName=parentCatalogName + File.separator + ss[ss.length - 1];
                txtCatalog.setCatalogName(parentCatalogName);
            }
            ls.add(txtCatalog);
            for(File fo: f.listFiles()){
                if(!fo.isDirectory())
                    continue;
                dirToCatalogs(parentCatalogName,fo.getAbsolutePath(),ls);
            }
            return;
        }
        else {
            return;
        }
    }
    /**
     * 获取图形块ShapeBlock级别的查询器
     * @return
     */
//    public abstract BlockQuerier getBlockQuerier();

    /**
     * 获取图形块ShapeBlock级别的更新器
     * @return
     */
//    public abstract BlockUpdater getBlockUpdater();
    /**
     * 将TxtAll转换成要素对象数组
     * @param txtAll 其中的TxtRawBytes，必须是SHP/DBF/SHX等文件的ZIP文件内容
     * @return
     * @throws Exception
     */
    public static List<SimpleFeature> toFeatures(TxtAll txtAll) throws Exception{
        return ShapeExtractor.parseToFeatures(txtAll.getTxtRawBytes());
    }

    /**
     * 将TxtAll转换成几何对象数组
     * @param txtAll 其中的TxtRawBytes，必须是SHP/DBF/SHX等文件的ZIP文件内容
     * @return
     * @throws Exception
     */
    public static List<Geometry> toGeometries(TxtAll txtAll) throws Exception{
        return ShapeExtractor.parseToGeometries(txtAll.getTxtRawBytes());
    }

    /**
     * 将TxtAll转换成GeoJSON数组
     * @param txtAll 其中的TxtRawBytes，必须是SHP/DBF/SHX等文件的ZIP文件内容
     * @return
     * @throws Exception
     */
    public static List<String> toGeoJSON(TxtAll txtAll) throws Exception{
        return ShapeExtractor.parseToStrings(txtAll.getTxtRawBytes());
    }

    /**
     * 将TxtAll转换成图形单元块
     * @param txtAll
     * @return
     * @throws Exception
     */
//    public static ShapeBlock toBlock(TxtAll txtAll) throws Exception{
//        return new ShapeBlock(txtAll);
//    }


    /**
     *
     * @param f  SHP文件，在相同的位置应该有有同名的DBF和SHX文件
     * @return
     * @throws Exception
     */
    public static Pair<TxtItem,TxtFile> parseTxtFiles(File f) throws Exception{
        if (!f.getName().endsWith("txt"))
            return null;
        //获取该SHP文件的边界矩形
        Envelope envelope = ShapeExtractor.parseToEnvelope(f.getCanonicalPath());
        if(envelope.isNull())
            return null;
        File dbfFile = new File(cn.edu.cug.cs.gtl.io.File.getDirectory(f.getAbsolutePath())+File.separator+
                cn.edu.cug.cs.gtl.io.File.getFileNameWithoutSuffix(f.getAbsolutePath())+".dbf");
        File shxFile = new File(cn.edu.cug.cs.gtl.io.File.getDirectory(f.getAbsolutePath())+File.separator+
                cn.edu.cug.cs.gtl.io.File.getFileNameWithoutSuffix(f.getAbsolutePath())+".shx");

        //检查是否存DBF、SHX和SHP
        if(!dbfFile.exists() || !shxFile.exists())
            return null;



        //设置SHPItem
        TxtItem txtItem = new TxtItem();
        txtItem.setTxtInternalId(BigDecimal.valueOf(-1));
        txtItem.setTxtEnTitle(f.getName());
        txtItem.setTxtCnTitle(f.getName());
        txtItem.setTxtExtent(
                new StringBuffer()
                        .append(String.valueOf(envelope.getMinX()))
                        .append(FileDataSplitter.COMMA.getDelimiter())
                        .append(String.valueOf(envelope.getMaxX()))
                        .append(FileDataSplitter.COMMA.getDelimiter())
                        .append(String.valueOf(envelope.getMinY()))
                        .append(FileDataSplitter.COMMA.getDelimiter())
                        .append(String.valueOf(envelope.getMaxY()))
                        .toString()
        );
        txtItem.setTxtCreateDate(new Date());
        txtItem.setTxtAppendDate(new Date());
        txtItem.setTxtUpdateDate(new Date());
        txtItem.setTxtType("SHP");
        //设置SHPFile
        TxtFile txtFile = new TxtFile();
        txtFile.setTxtInternalId(txtItem.getTxtInternalId());
        //将DBF、SHX和SHP压缩成ZIP文件，并读取文件内容为byte[]
        List<File> fileToArchive = new ArrayList<>();
        fileToArchive.add(f);
        fileToArchive.add(dbfFile);
        fileToArchive.add(shxFile);
        byte[] zipBytes = ZipUtils.zip(fileToArchive);
        txtFile.setTxtRawBytes(zipBytes);
        return new Pair<>(txtItem,txtFile);
    }

    public abstract List<BigDecimal> getTxtInternalIdsByCatalogInternalId(BigDecimal catalogId);

    /**
     * 获取分类目录树
     * @return
     */
//    public CatalogTree getCatalogTree()  {
//        try {
//            List<TxtCatalog> txtCatalogs =getCatalogs();
//            DatabaseMetaData metaData = session.getDataSource().getConnection().getMetaData();
//            if(metaData!=null )
//                return new CatalogTree(txtCatalogs,metaData.getURL());
//            else
//                return new CatalogTree(txtCatalogs);
//        }
//        catch (SQLException e){
//            e.printStackTrace();
//        }
//        return null;
//    }



//    public List<BigDecimal> getTxtInternalIdsByCatalogInternalId(BigDecimal catalogId){
//        if(session.isClosed()){
//            session = Session.getMasterSession();
//        }
//        TxtItemMapper txtItemMapper = session.getMapper(TxtItemMapper.class);
//        return txtItemMapper.getTxtInternalIdsByCatalogInternalId(catalogId);
//    }

}
